Repository navigation
Conversation
Add provider methods and types for the verification endpoints of
Cloud Registry 0.4.0 and later (GET /verifications, GET, PUT and
DELETE /services/{id}/verification), optional in the interface so
that custom providers still conform, and helpers that tell whether a
verification still matches its service, as the registry compares
them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add ecc-client-elixir-cloud-registry-service-verification: a badge for verified services, and "Verify" and "Remove verification" for admins. It sends the URL and API type of the service shown as the ones checked, takes a verification the app has loaded already (e.g. for a list, from getVerifications()), reacts to a provider set after the first update, and fires change and failure events with the status and conflict code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add code to CloudRegistryError, so that apps and the verification component read the conflict code of a 409 without digging into the problem. Test the component's busy state, races and failures of verify and remove, and link it from the package introduction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload while a verify or remove ran (e.g. a new provider after a token refresh) dropped the result: no change event, the old state shown, and the button enabled again. Track loads apart from verify and remove, always report a result the registry has applied, and show it until the app sets verification again. Fold capital sharp s and dotless i as Python does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Track running verify and remove requests by service ID, so that one for a previous service doesn't block the service shown, and show a result (also one kept over the app's verification) only while the component shows the same service of the same registry (base URL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideData structuresDefine the verification data types and optional provider contract, and preserve conflict codes on registry errors so callers can interpret verification failures.
Expose the verification event detail types so applications can type-check the component’s changed and failed events.
APIAdd the REST provider operations for listing, fetching, creating, and deleting service verifications, including encoded service IDs and registry errors.
Export the verification types and comparison helpers through the providers package entry point.
Register and export the service-verification custom element as a public component.
ImplementationImplement verification matching and list filtering using the registry’s URL and Python-style type comparison rules.
Implement component loading, admin actions, stale-request handling, failure reconciliation, event dispatch, and rendering for service verification.
DocumentationDocument component usage, provider methods, matching helpers, types, errors, properties, events, and styling, and link the feature from the Cloud Registry introduction.
TestsTest REST verification requests, matching semantics, and filtering of verified services.
Exercise component rendering, actions, errors, provider changes, and request races.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Documentation build overview
49 files changed ·
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #578 +/- ##
==========================================
+ Coverage 71.40% 75.55% +4.15%
==========================================
Files 23 36 +13
Lines 5655 8846 +3191
Branches 199 470 +271
==========================================
+ Hits 4038 6684 +2646
- Misses 1610 2152 +542
- Partials 7 10 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Linked issuesChecked at commit 5614d12 #459 feat(cloud-registry): verifications · 2 of 3 met
|
There was a problem hiding this comment.
Hey - I've found 8 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="301" />
<code_context>
+ this.own = { id, verification, provider };
+ }
+ }
+ this.changed(id, verification);
+ }
+
</code_context>
<issue_to_address>
**Old registry results corrupt new registry state**
When a mutation is pending while the app switches registries with an overlapping service ID, and the old response arrives afterward, `changed()` emits only the service ID and verification, so a list handler cannot distinguish the old registry’s result from the new registry’s. It can store or delete verification state in the new registry’s map, leaving the list with incorrect verification state.
Include registry identity in the event detail and have list handlers apply results only to the matching registry.
Also at `packages/ecc-client-elixir-cloud-registry/src/events/ecc-service-verification-changed.ts:5-7`, `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:386`.
</issue_to_address>
### Comment 2
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="224-229" />
<code_context>
+ this.loadRequest += 1;
+ const request = this.loadRequest;
+ const id = this.targetId;
+ if (this.verification !== undefined) {
+ const { own } = this;
+ this.current =
+ own && this.showsSame(own.id, own.provider)
+ ? own.verification
+ : this.verification;
+ return;
+ }
</code_context>
<issue_to_address>
**Old registry badge appears on new registry**
When the provider changes to a different registry while `verification` still contains a value loaded from the previous registry, `load()` treats any defined `verification` as authoritative without associating it with a registry. If the provider switches from registry A to registry B before the app replaces that property, the component skips B's GET and shows A's matching verification as B's, so users see a false Verified badge.
Scope supplied verifications to their registry and discard or reload them when the component switches to a different registry.
</issue_to_address>
### Comment 3
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="186" />
<code_context>
+ changedProperties.has("baseUrl") ||
+ changedProperties.has("serviceId") ||
+ changedProperties.has("verification") ||
+ (changedProperties.has("service") && previous?.id !== this.service?.id)
+ ) {
+ this.load();
</code_context>
<issue_to_address>
**Stale verification reappears as verified**
When the service’s URL or API type changes and later returns to its previously verified values without changing its ID, `updated()` skips `load()` when the service changes but keeps the same ID, leaving `current` unchanged. When the service returns to its old values, `matchesVerification()` matches that stale result and the component shows “Verified” even though the registry removed the verification.
Reload or clear `current` whenever the service changes, including changes to its URL or API type.
Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:227-229`.
</issue_to_address>
### Comment 4
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="340" />
<code_context>
+ this.applied(id, verification, provider);
+ } catch (err) {
+ this.setPending(id, false);
+ this.fail(failureDetail(id, "verify", err, "Failed to verify service"));
+ if (
+ statusOf(err) === 409 &&
</code_context>
<issue_to_address>
**Mutation failures leave stale state**
When a verify or remove is applied by the registry but the client receives a timeout, connection failure, or error response, `verifyService()` and `removeVerification()` treat every non-conflict/non-404 rejection as a failed mutation without reconciling state. If the registry applied the change but the response was lost or errored, the component and any event-driven app map keep showing the old verification state.
Reconcile the verification after ambiguous mutation failures, while still reporting the failed request.
Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:373`.
</issue_to_address>
### Comment 5
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="301" />
<code_context>
+ this.own = { id, verification, provider };
+ }
+ }
+ this.changed(id, verification);
+ }
+
</code_context>
<issue_to_address>
**Older mutation overwrites newer state**
When separate component instances or tabs mutate the same service and responses arrive in a different order from the registry’s applied changes, `applied()` emits each mutation result as it arrives, with no ordering or version check. If two instances mutate the same service and the registry applies PUT then DELETE but the responses arrive in reverse order, an event-driven consumer can apply the old PUT result last and show the service as verified when it is not.
Reconcile state after mutation events or include and compare a server ordering/version value before applying results.
</issue_to_address>
### Comment 6
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="298-299" />
<code_context>
+ this.loadRequest += 1;
+ this.current = verification;
+ if (this.verification !== undefined) {
+ this.own = { id, verification, provider };
+ }
+ }
+ this.changed(id, verification);
</code_context>
<issue_to_address>
**Successful mutation result disappears**
When a verify or remove succeeds with `verification` undefined, then the provider is replaced for the same registry, `applied()` does not save the result in `own`, and `load()` clears `current` before refetching. A 404, failed, or stale refetch then hides the result of the successful mutation.
Save successful mutation results in `own` even when `verification` is undefined, so `load()` can reuse them after a provider change.
</issue_to_address>
### Comment 7
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="163" />
<code_context>
+
+ /** Whether a verify or remove of the service shown is running. */
+ private get busy(): boolean {
+ return this.pending.has(this.targetId);
+ }
+
</code_context>
<issue_to_address>
**Old registry request disables new actions**
When a request is pending while the provider switches to another registry with the same service ID, `pending` and `busy` use only the service ID, so the old registry’s request keeps Verify or Remove disabled for the new registry until it settles. A hung request leaves the new registry’s actions disabled indefinitely.
Key pending work by both registry and service ID, and have `busy` check the current registry’s key.
Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:164`, `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:277-280`.
</issue_to_address>
### Comment 8
<location path="apps/documentation/pages/docs/cloud-registry/components/service-verification.mdx" line_range="120" />
<code_context>
+After a verify or remove, the component shows the result for that service until the app sets `verification` again, also if the provider is replaced meanwhile by one for the same registry (the same base URL, e.g. with a new token); update your verifications from `ecc-service-verification-changed`, as above. Pass a stable value (e.g. the object from the map, not one built on each render): every new value counts as set again.
+
+<Callout type='info'>
+ The services list of the GA4GH Service Registry package (`ecc-client-ga4gh-service-registry-services`) doesn't show verifications itself; place the component in a row's `actions-<id>` slot or in your own list.
+</Callout>
+
</code_context>
<issue_to_address>
**Badge missing from list**
The issue asks for a verified badge in the services list, but this change adds only a standalone component and says the list does not show verifications itself. Apps must wire the component into a row manually, so the list requirement remains partly met.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 8 findings to address first, and an admin can create a persistent verification record that the component displays as a badge, so a wrong verification could mislead users even after this code is reverted. The record is bounded and can be removed through the registry, but reverting the component alone does not remove it.
Blocking findings: packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:301, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:229, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:186, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:340, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:301, and 3 more
Address automated review: load the verification again when the URL or API type of the service changes and after a verify or remove fails without a clear outcome (no status or a server error), and key running requests by registry and service, so that a request to one registry doesn't block another. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Apps can show and manage Cloud Registry verifications (cloud-registry 0.4.0 and later) without their own copy of the provider.
Provider (
@elixir-cloud/cloud-registry/providers)getVerifications(),getServiceVerification(id),verifyService(id, verification?)anddeleteServiceVerification(id)forGET /verificationsandGET/PUT/DELETE /services/{id}/verification, matching the registry's OpenAPI (additions.openapi.yaml). They are optional inCloudRegistryProvider, so custom providers without them still conform.Verification,ServiceVerificationandVerificationRegister.matchesVerification()andverifiedServices(): a verification only counts while the service still has the verified URL (as written) and API type (group and artifact, trimmed and case-insensitive, as the registry compares them with Python'sstrip().casefold()). They are ported from the Cloud Registry UI'slib/verifications.ts.CloudRegistryError.code: the problemcodeof a conflict, e.g.verification-mismatchorpending-service.Component
ecc-client-elixir-cloud-registry-service-verificationadminproperty: admins see "Not verified", "Verify" and "Remove verification". "Verify" sends the URL and API type ofserviceas the checked values, so a service that changed in the meantime isn't verified (409).verificationproperty for lists: the app loads all verifications with one request and passes each row's verification, so the rows make no requests of their own.providerorbaseUrlset after the first update, the behaviour fix: react to provider set after the first update #565 asks for in other components. It ignores stale answers, tracks running verify and remove requests per service, and still reports a result that arrives after a reload, but shows it only for the same service of the same registry.ecc-service-verification-changedandecc-service-verification-failed. The failure event carries the action, status, problem details and conflict code, the same way the service-create events carry them.Docs: a new component page, a Verifications section and types in the providers page, the
codeproperty in the Errors table, and a link in the package introduction.Verified badge in the services list: the issue asks for it, but the services list (
ecc-client-ga4gh-service-registry-services) lives in the service-registry package. #455 is reworking that list (filters, the verified filter, a per-row render hook), and #565 also changes it. This PR therefore doesn't touch the list. Apps put the component in a row'sactions-<id>slot, or in their own list, with the preloaded verification. The docs show this. Once #455 adds its row hook, the badge can go there without any change to this component.Closes #459
Verification
All commands were run in
packages/ecc-client-elixir-cloud-registryunless noted.npm run test -- --coverage: 95 tests passed in Chromium and Firefox. WebKit can't run on this machine: Playwright doesn't support its OS (Ubuntu 26.04), so CI covers WebKit. Line coverage of the new code:verifications.ts100%,service-verification.ts450/454.npm run build: passes, and generates the component, its React wrapper and the custom elements manifest.npm run lintat the repo root (husky pre-commit, all packages): 0 errors.npx tsc -p tsconfig.prod.json: no errors.npm run buildinapps/documentation: passes, and the new page renders as/docs/cloud-registry/components/service-verification.npx cspellon the cloud-registry docs pages: 0 issues.adversarial-reviewerandquality-reviewer, in two rounds. Fixed in round 1:verificationcame back after a reload;codewas missing onCloudRegistryError;verification). It also adds tests for removing a verification of a previous service, and docs notes on reloading and on passing stable values.verification, the badge in the services list). CI was green again after the fixes.Declined review findings
failureDetail(quality, medium). It is similar to the one in service-create. Sharing it means refactoring service-create, which is outside this issue, and other open work touches the package's components. A shared helper inproviders/errors.tswould fit a follow-up.trim().toLowerCase()instead of emulatingcasefold()(quality, low). The client should agree with the registry, which compares withstrip().casefold(), and the Cloud Registry UI already uses this approach. The reviewer's brute-force check found only the two exceptions that this PR now handles.2.0.0-alpha.51(quality, low). This is the next release, the first one that contains the component. Other pages give the version that introduced their component.loadRequestbump inapplied()stays as a safeguard, with a comment.Open questions
None.
🤖 Generated with Claude Code